#include <stdio.h> 
#include <stdlib.h>
#include <math.h> 
#include <mpi.h>

// Note: large N*N array was removed in order to simplify code for 
// milestone 1. That code has been saved for use in milestone 2

// ****** SPEEDUP ****** make all floats instead of double? 

int main(int argc, char *argv[])
{

    int myRank, procNum; 
	MPI_Init(&argc,&argv);
	MPI_Comm_rank(MPI_COMM_WORLD,&myRank);
	MPI_Comm_size(MPI_COMM_WORLD,&procNum);
    

	if(myRank == 0)
	{
		//printf("Size is: %d\n", procNum); 
		if(argc != 3)
		{
			 printf("Not enough or too many arguments\nExpecting 2");  
			return 1; 
		}
	}

	unsigned int N; 
    int A; 
    N = atoi(argv[1]); 
    A = atoi(argv[2]);
    
	double limit = N*N/3; 
    unsigned int normalLimit = N/10; 

    double thetaStep = (double)normalLimit/(double)limit;

	double maxX = 0; 
	double maxY = 0; 
	double maxMag = 0; 

    double xCord = 0; 
    double yCord = 0; 
    double magnitude = 0; 
    double theta = 0; 

    // Gives each processor a different section of theta 
	for(double t=myRank*normalLimit/procNum; t<(myRank+1)*normalLimit/procNum; t+=thetaStep)
    {
        // ****** SPEEDUP ****** Simplify this math? 
        theta = t;  //*M_PI/180; 
        xCord = A*(cos(theta)+theta*sin(theta));        
        yCord = A*(sin(theta)-theta*cos(theta)); 
        magnitude = sin(theta)*sin(theta);
        
		// Finds largest Z(x, y) and saves Z, x and y. 
		if(magnitude > maxMag)
		{
			maxMag = magnitude; 
			maxX = xCord; 
			maxY = yCord; 
		}
    }
    
	//printf("Proc %d\tMag = %f\tX = %f\tY = %f\n", myRank, maxMag, maxX, maxY); 

	// Communication vector setup
	double * magnitudes = (double*)malloc(sizeof(double)*procNum);
	double * xValues = (double*)malloc(sizeof(double)*procNum);
	double * yValues = (double*)malloc(sizeof(double)*procNum);
	// Sends max Z values and their coordinates to root node
	// Root node finds the highest of them all
	MPI_Gather((void*)&maxMag, 1, MPI_DOUBLE, magnitudes, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
	MPI_Gather((void*)&maxX, 1, MPI_DOUBLE, xValues, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
	MPI_Gather((void*)&maxY, 1, MPI_DOUBLE, yValues, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);

	if(myRank == 0)
	{
		double finalMaxMag = 0; 
		int maxIndex = 0; 
		
		printf("Max From each Processor\n"); 
		for(int i=0; i<procNum; i++)
		{
			//printf("Proc %d\tMag = %f\tX = %f\tY = %f\n", i, magnitudes[i], xValues[i], yValues[i]); 
			if(magnitudes[i] > finalMaxMag)
			{
				finalMaxMag = magnitudes[i];
				maxIndex = i; 
			}
		}
		// Prints highest Z value AND the corresponding X and Y value 
		printf("\nMax Z(x,y) is: %f at X = %f  Y = %f \n", magnitudes[maxIndex], xValues[maxIndex], yValues[maxIndex]);
	}
	MPI_Finalize();    
    return 0; 
}
    
